#!/usr/bin/perl

######################################################################
package Swignition::uF::hCalendar;
######################################################################

use CGI::Util;
use Swignition::GenericParser::Utils;
use Swignition::uF;
use Swignition::uF::hEvent;
use Swignition::uF::hTodo;
use Swignition::uF::hAlarm;
use Swignition::uF::hFreebusy;
use XML::LibXML qw(:all);
use strict;

BEGIN
{
	our $VERSION = 0.1;
	our @ISA = qw(Swignition::uF);
}

sub cc
{
	$_ = shift;
	s/\-([a-z])/uc($1)/eg;
	return $_;
}

sub consume
{
	my $page   = shift;

	unless ( $page->{Profiles}->{'http://dannyayers.com/microformats/hcalendar-profile'}
	||       $page->{Profiles}->{'http://www.w3.org/2002/12/cal/hcal'}
	||       $page->{Profiles}->{'http://purl.org/uF/hCalendar/1.0/'}
	||       $page->{Profiles}->{'http://purl.org/uF/hCalendar/1.1/'}
	||       $page->{Profiles}->{'http://purl.org/uF/2008/03/'}
	||      !$page->{options}->{uf_strict_profiles})
	{
		return;
	}
	
	$page->{uF}->{hCalendar} = [ Swignition::uF::hCalendar::parse_all($page) ];
	foreach my $hcalendar (@{ $page->{uF}->{hCalendar} })
	{
		$page->mergeSubject( $hcalendar->rdf_subject );

		foreach my $childtype (qw(vevent vtodo valarm vfreebusy))
		{
			foreach my $h ( @{ $hcalendar->{$childtype} } )
			{
				$page->mergeSubject( $h->rdf_subject );
				
				foreach my $persontype (qw(contact attendee organizer))
				{
					foreach my $hcard (@{ $h->{$persontype} })
					{
						next unless (UNIVERSAL::can($hcard, 'rdf_subject'));
					
						$page->mergeSubject($hcard->rdf_subject);
						
						foreach my $c (@{$hcard->{tel}})
							{ $page->mergeSubject($c->rdf_subject); }
						foreach my $c (@{$hcard->{email}})
							{ $page->mergeSubject($c->rdf_subject); }
						foreach my $c (@{$hcard->{org}})
							{ $page->mergeSubject($c->rdf_subject); }
						foreach my $c (@{$hcard->{impp}})
							{ $page->mergeSubject($c->rdf_subject); }
					}
				}
				
				if ($childtype eq 'vfreebusy')
				{
					foreach my $c (@{$h->{freebusy}})
						{ $page->mergeSubject($c->rdf_subject); }
				}
			}
		}
	}

	if (defined $page->{uF}->{hCalendar}->[0])
		{ $page->mergeNS( $page->{uF}->{hCalendar}->[0] ); }
}


sub parse_all
{
	my $page   = shift;
	my $within = shift || $page->{DOM};
	my @nodes  = Swignition::GenericParser::Utils::searchClass('vcalendar', $within);
	my @rv;
	
	if (!@nodes && $within->nodeType==XML_DOCUMENT_NODE)
	{
		push @nodes, $within->getDocumentElement ;
	}
	
	foreach my $hc (@nodes)
	{
		my $hc_parsed = parse($page, $hc);
		push @rv, $hc_parsed; 
	}
	
	return @rv;
} #/sub parse_all


sub uri
{
	my $this = shift;
	my $all  = shift;
	my @rv;
	
	if ($this->{_dom}->nodeType == XML_DOCUMENT_NODE
	 || $this->{_dom}->tagName eq 'html'
	 || $this->{_dom}->tagName eq 'body')
	{
		push @rv, $this->{_page}->uri;
	}
	elsif ($this->{_id} =~ /^http\:/)
	{
		push @rv, $this->{_id};
	}
	elsif (length $this->{_id})
	{
		push @rv, $this->{_page}->uri.'#'.$this->{_id};
	}
	push @rv, Swignition::GenericParser::Utils::BNodeURI($this->{_dom}, 'Calendar')
		unless (@rv);
	
	return $rv[0] unless (defined $all);
	while ($all) { shift @rv; $all--; }
	return @rv;
} #/sub uri


sub rdf_subject
{
	my $this    = shift;
	my $ns      = 'http://buzzword.org.uk/rdf/icalx#';

	# Create RDF subject
	my $subject = Swignition::RDFModel::Subject->new($this->uri);
	$subject->setDom($this->{_dom});
	
	# Patterns for simple data.
	my @_SINGLES  = qw(version prodid x-wr-calname x-wr-caldesc calscale method);
	my @_OBJECTS  = qw(vevent vtodo valarm vfreebusy vjournal);
	
	my $doIt = 0;

	# Embedded objects which may be represented as resources
	foreach my $property (@_OBJECTS)
	{
		foreach my $V (@{ $this->{$property} })
		{
			my $val;
			if (ref $V)
				{ $val = Swignition::RDFModel::Value->new($V->uri, 1); }
			else
				{ $val = Swignition::RDFModel::Value->new($V); }
				
			if ($property =~ /\b(version|prodid|calscale|method)\b/)
				{ $subject->addVal('http://www.w3.org/2002/12/cal/ical#'.$property, $val); }
			else
				{ $subject->addVal($ns.cc($property), $val); }
			
			$doIt++;
		}
	}
	
	if ($doIt || ($this->{_dom}->tagName ne 'html'))
	{
		$subject->addObject($this);
		$subject->addVal('http://www.w3.org/1999/02/22-rdf-syntax-ns#type',
			Swignition::RDFModel::Value->new($ns.'Vcalendar', 1));
	
		$subject->addVal('http://purl.org/dc/terms/title', 
			Swignition::RDFModel::Value->new($this->{_dom}->getAttribute('title')))
			if (length $this->{_dom}->getAttribute('title'));
	
		# Properties which exist as singles.
		foreach my $property (@_SINGLES)
		{
			next unless (length $this->{$property});
			my $val = Swignition::RDFModel::Value->new($this->{$property});
			$subject->addVal($ns.$property, $val);
		}	
	}
	
	return $subject;
} #/sub rdf_subject


sub metadata_ns
{
	my $this = shift;
	return {
		'i'  => { nsurl=>'http://www.w3.org/2002/12/cal/ical#',    title=>'RDF Calendar' },
		'ix' => { nsurl=>'http://buzzword.org.uk/rdf/icalx#', title=>'RDF Calendar (Extentions)' }
		};
} #/sub metadata_ns


sub parse
{
	my $page = shift;
	my $rv = { '_dom'=>shift };
	my $pkg = __PACKAGE__;
	if (defined $page->{uF}->{_Shortcuts}->{$pkg}->{ $rv->{_dom}->getAttribute('_xpath') })
		{ return $page->{uF}->{_Shortcuts}->{$pkg}->{ $rv->{_dom}->getAttribute('_xpath') }; }
	else
		{ $page->{uF}->{_Shortcuts}->{$pkg}->{ $rv->{_dom}->getAttribute('_xpath') } = $rv; }
	
	my $hc_node = $rv->{'_dom'}->cloneNode(1);

	my $id = $hc_node->getAttribute('id');
	$rv->{_id} = $id if (length $id);
	$rv->{_page} = $page;
	
	data_patterns($page, $hc_node, 2);

	# Pull out nested hEvents
	my @events = Swignition::uF::hEvent::parse_all($page, $hc_node);
	$rv->{vevent} = \@events unless (!@events);

	# Pull out nested hTodo.
	my @events = Swignition::uF::hTodo::parse_all($page, $hc_node);
	$rv->{vtodo} = \@events unless (!@events);
	
	# Pull out nested hAtom Entries.
	my @events = Swignition::uF::hEntry::parse_all($page, $hc_node);
	$rv->{vjournal} = \@events unless (!@events);
	
	# Pull out nested hAlarm.
	my @events = Swignition::uF::hAlarm::parse_all($page, $hc_node);
	$rv->{valarm} = \@events unless (!@events);
	
	# Pull out nested hFreebusy.
	my @events = Swignition::uF::hFreebusy::parse_all($page, $hc_node);
	$rv->{vfreebusy} = \@events unless (!@events);
	
	simple_parse(
		$rv,
		$hc_node,
		[
			['calscale',     '?'],   # extension
			['method',       '?'],   # extension
			['prodid',       '?'],   # extension
			['version',      'n?'],  # extension
			['x-wr-caldesc', '?'],   # extension
			['x-wr-calname', '?'],   # extension
		]
	);
	
	# Figure out nesting levels and use them to imply relationships.
	my %xpath;
	foreach my $component (qw(vevent vtodo vjournal valarm vfreebusy))
	{
		foreach my $object (@{ $rv->{$component} })
		{
			my $xp = $object->{'_dom'}->getAttribute('_xpath');
			$xpath{$xp} = $object;
		}
	}
	my @xpaths = keys %xpath;
	foreach my $xp (@xpaths)
	{
		unless (defined $xpath{$xp}->{'parent'})
		{
			my $parent = _findParent($xp, @xpaths);
			if ($parent)
			{
				$xpath{$xp}->{'parent'} = $xpath{$parent}->uri;
				push @{ $xpath{$parent}->{'child'} }, $xpath{$xp}->uri;
			}
		}
	}
	
	bless $rv;
	Swignition::uF::error_list_process($rv, $page, 'http://microformats.org/wiki/User:TobyInk/hcalendar-1.1');
	return $rv;
	
} #/sub parse


sub _findParent
{
	my $x = shift;
	my $longest = '';
	
	foreach my $potential (@_)
	{
		if (_ancestorOf($potential, $x))
		{
			$longest = $potential
				if (length($potential) > length($longest));
		}
	}
	
	return $longest;
}

sub _ancestorOf
{
	my ($a, $b) = @_;
	return if ($a eq $b);
	return (substr($b, 0, length($a)) eq $a);
}

sub parsePerson
{
	my $page  = shift;
	my $dom   = shift;

	my $hcard = Swignition::uF::hCard::parse($page, $dom, 1);	
	return $hcard
		if (length $hcard->{fn});

	my $contents = STRINGIFY($dom, 'value');

	if ($dom->getAttribute('href') =~ /^mailto\:/i)
	{
		my $em =  $dom->getAttribute('href');
		$em =~ s/^mailto.//i;
		return bless({
				'_dom'   => $dom,
				'_page'  => $page,
				'_id'    => $dom->getAttribute('id'),
				fn       => $contents,
				email    => [ bless({ value => $em, '_dom'   => $dom }, 'Swignition::uF::hCard::email') ]
			},
			'Swignition::uF::hCard');
	}

	elsif (length $dom->getAttribute('href'))
	{
		return bless({
				'_dom'   => $dom,
				'_page'  => $page,
				'_id'    => $dom->getAttribute('id'),
				fn       => $contents,
				url      => [ $page->uri($dom->getAttribute('href'), $dom) ]
			},
			'Swignition::uF::hCard');
	}
	
	return bless({
			'_dom'   => $dom,
			'_page'  => $page,
			'_id'    => $dom->getAttribute('id'),
			fn       => $contents
		},
		'Swignition::uF::hCard');
}


1; #/package Swignition::uF::hCalendar




